﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using GameStateManagement;
using BHG.System;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Controllers;
using FarseerPhysics.Dynamics.Contacts;

namespace BHG.GameData
{
    class Level01 : GameplayScreen
    {
        protected const float BH_RADIUS = 2.0f;
        protected float BH_STRENGHT_BOOST = 2.0f;
        protected const float CAMERA_SPEED_LVL01 = 3.0f;       // camera offset per second
        protected const float BLACK_HOLE_SCALE_PS = 0.0025f*60*2;
        protected const float MOVE_CAMERA_INSIDE = -0.8f * 60;
        protected const float WHEN_MOVE_CAMERA_IN_BLACK_HOLE = 2.35f*2f;
        protected const float WHEN_LOAD_NEXT_LEVEL = 3f;

        protected readonly Vector2 BH_POSITION = new Vector2(65.0f, 25.0f);
        

        Model redcube;
        Model cube;
        Model lampa;
        Model house;
        Model asfaltPruh;
        Model lavicka;

        GravityController BH;
        bool blackHole = false;
        BallObject blackHoleObject;
        int blackHoleStrenght = 10;

        Song song;

        SoundEffect DepartureBlackHole;

        /// <summary>
        /// load ALL content
        /// </summary>
        public override void LoadContent() {
            base.LoadContent();
            MediaPlayer.Stop();

            PhysicsEngine.Instance.Gravity = DEFAULT_WORLD_GRAVITY;

            redcube = content.Load<Model>("Models/Cubes/krychle-cihla");
            //redcubebig = content.Load<Model>("Models/Cubes/krychle-cihla-pokus");
            cube = content.Load<Model>("Models/Lands/Grass/grass1");
            asfaltPruh = content.Load<Model>("Models/Lands/Road/asfalt-pruh");
            lampa = content.Load<Model>("Models/Backgrounds/Lamps/lampa1");
            lavicka = content.Load<Model>("Models/Backgrounds/Benches/lavicka1");
            house = content.Load<Model>("Models/Backgrounds/Houses/dum2");

            DepartureBlackHole = content.Load<SoundEffect>("Sound/sounds/Departure Black Hole");
            song = content.Load<Song>("Sound/music/12 Scratch 95 Master");


            CubeObject lavickaObject = new CubeObject(
                               new Vector2(
                                   35,
                                   15),
                               new Vector2(
                                   10f,
                                   3f),
                                   0f,
                               true,
                               lavicka,
                               3f
                               );
            lavickaObject.Dislocation = new Vector3(0, -1f, 0);
            objectsOnMap.Add(lavickaObject);
            this.BaseLand(-60f, 0f);
            this.GraphicWall(0f, 0f);
            this.Barrier(30f, 1f);
            MediaPlayer.Play(song);
        }

        /// <summary>
        ///  Update base, then update level.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="otherScreenHasFocus"></param>
        /// <param name="coveredByOtherScreen"></param>
        public override void Update( GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen ) {
            // generic update, update physics, call only once
            base.Update(gameTime, otherScreenHasFocus, false);

            if (player.Position.X < -10)
            {
                player.body.LinearVelocity = new Vector2(1f, player.body.LinearVelocity.Y);
            }

            if (player.Position.X > 90)
            {
                player.body.LinearVelocity = new Vector2(-1f, player.body.LinearVelocity.Y);
            }

            // Player reached certain position we create BH
            if ( ( player.Position.X > 80 ) && ( blackHole == false ) ) {
                MediaPlayer.Stop();
                DepartureBlackHole.Play();

                // add black hole
                blackHoleObject = new BallObject(BH_POSITION, BH_RADIUS, 0.0f, false, BlackHoleModel, 0.2f,300);
                BH = PhysicsEngine.Instance.AddGravityCenter(blackHoleObject, GravityType.DistanceSquared, blackHoleStrenght);
                objectsOnMap.Add(blackHoleObject);

                blackHoleObject.body.FixtureList[0].OnCollision += onMyBlackHoleCollision;

                blackHole = true;
                cameraMove = true;
                PlaySteps = false;
            }

            if ( blackHole == true ) {
                float time = (float) gameTime.ElapsedGameTime.TotalSeconds;

                blackHoleObject.Rotation += 0.1f;
                blackHoleObject.RotationY += 0.01f;
                blackHoleObject.RotationZ += 0.01f;

                BH.Strength += BH_STRENGHT_BOOST;

                Vector3 delta =   camera.lookAt - new Vector3(BH_POSITION, 0);
                delta.Normalize();
                delta *= CAMERA_SPEED_LVL01 * time;

                camera.moveCamera(delta.X, delta.Y, CAMERA_SPEED_LVL01 * time);

                blackHoleObject.Scale += BLACK_HOLE_SCALE_PS * time;
                if (blackHoleObject.Scale > WHEN_MOVE_CAMERA_IN_BLACK_HOLE)
                {
                    camera.moveCamera(0,0,MOVE_CAMERA_INSIDE * time);
                    if (camera.lookAt.Z < WHEN_LOAD_NEXT_LEVEL)
                    {
                        LoadingScreen.Load(ScreenManager, true, PlayerIndex.One, CurrentGame.createNewLevel(2));
                    }
                }
                // we change all static bodies to dynamic
                foreach ( Body body in PhysicsEngine.Instance.gameWorld.BodyList ) {
                    if ( ( body.Position.Y > 0f ) && ( body.Position.X > 10 ) && ( body.Position.Y < 25 ) && ( body.Position.Y > 22 - blackHoleObject.Scale * 10f ) ) {
                        body.IsStatic = false;
                    }
                }
            }
        }

        public void Barrier( float positionX, float positionY ) {
            int sizeY = 5;
            int sizeX = 35;
            int sizeYDown = 2;
            float thick = 2f;

            float nextLevel = 36f;

            //First level
            for (int i = 0; i < sizeYDown; i++)
            {
                for (int j = 0; j < sizeX; j++)
                {
                    objectsOnMap.Add(new CubeObject(
                        new Vector2(
                            positionX + j * thick,
                            positionY + i * thick),
                        new Vector2(
                            thick,
                            thick),
                        false,
                        redcube));
                }
            }

            //Second level
            for ( int i = 0; i < sizeY + 1; i++ ) {
                for ( int j = 0; j < sizeX / 2; j++ ) {
                    objectsOnMap.Add(new CubeObject(
                        new Vector2(
                            positionX + nextLevel + j * thick,
                            positionY + i * thick + sizeYDown * thick),
                        new Vector2(
                            thick,
                            thick),
                        false,
                        redcube));
                }
            }
        }

        /// <summary>
        /// Create graphic wall of the whole world
        /// </summary>
        /// <param name="positionX"></param>
        /// <param name="positionY"></param>
        public void GraphicWall( float positionX, float positionY ) {

               objectsOnMap.Add(new GraphicObject(
                        new Vector2(
                            positionX,
                            positionY),
                        house,
                        -70f, 
                        0.0018f,
                        0f,
                        -1.6f,
                        0f));
               objectsOnMap.Add(new GraphicObject(
                        new Vector2(
                            positionX+135f,
                            positionY),
                        house,
                        -70f,
                        0.0018f,
                        0f,
                        -1.6f,
                        0f));
        }

        /// <summary>
        /// Create the base objects where you can walk
        /// </summary>
        /// <param name="positionX"></param>
        /// <param name="positionY"></param>
        public void BaseLand(float positionX, float positionY)
        {
            //Here you can walk
            int multiplyHigh = 40;
            float thick = 10f;
            float thickUp = 1f;
            for (int i = 0; i < multiplyHigh; i++)
            {
                objectsOnMap.Add(new CubeObject(
                    new Vector2(
                        positionX + i * thick,
                        positionY),
                    new Vector2(
                        thick,
                        thickUp),
                    false,
                    cube
                    ));
            }

            //This is graphic land
            for (int i = 0; i < multiplyHigh; i++)
            {

                for (int j = 0; j < 3; j++)
                {
                    objectsOnMap.Add(new GraphicObject(
                        new Vector2(
                            positionX + i * thick,
                            positionY),
                        cube,
                        -thick - thick * j));
                }
                for (int j = 0; j < 5; j++)
                {
                    objectsOnMap.Add(new GraphicObject(
                        new Vector2(
                            positionX + i * thick,
                            positionY),
                        cube,
                        2 * thick + thick * j));
                }
                objectsOnMap.Add(new GraphicObject(
                    new Vector2(
                        positionX + i * thick,
                        positionY),
                    asfaltPruh,
                    thick));
            }

            //lamps
            for (int i = 0; i < multiplyHigh/3; i++)
            {
                objectsOnMap.Add(new GraphicObject(
                         new Vector2(
                             positionX + thick*i*3,
                             positionY),
                         lampa,
                         thick/3f, 1f));
                objectsOnMap.Add(new GraphicObject(
                         new Vector2(
                             positionX + thick * i * 3,
                             positionY),
                         lampa,
                         2*(thick / 3f)+thick, 1f));
            }

            //benches
            for (int i = 0; i < multiplyHigh / 3; i++)
            {
                objectsOnMap.Add(new GraphicObject(
                         new Vector2(
                             positionX + thick * i * 3,
                             positionY),
                         lavicka,
                         - thick, 
                         3f));
            }
        }

        /// <summary>
        /// simple wall
        /// </summary>
        /// <param name="positionX"></param>
        /// <param name="positionY"></param>
        public void Wall( float positionX, float positionY ) {
            int multiplyHigh = 15;
            float multiply = 2f;
            int depth = 4;
            //Vytvori stenu z cervenych krychlicek na zacatku levlu, aby hrac nemohl odejit
            for ( int i = 0; i < multiplyHigh; i++ ) {
                //touchable layout
                objectsOnMap.Add(
                    new CubeObject(
                        new Vector2(
                            positionX,
                            positionY + i * multiply),
                        new Vector2(
                            multiply,
                            multiply),
                        0.0f,
                        false,
                        redcube));

                //graphic layout
                for ( int j = 0; j < depth; j++ ) {
                    objectsOnMap.Add(new GraphicObject(
                        new Vector2(
                            positionX,
                            positionY + i * multiply),
                        redcube,
                        multiply + j * multiply));
                    objectsOnMap.Add(new GraphicObject(
                        new Vector2(
                            positionX,
                            positionY + i * multiply),
                        redcube,
                        -multiply - j * multiply));
                }
            }
        }

        protected bool onMyBlackHoleCollision(Fixture fixtureA, Fixture fixtureB, Contact manifold)
        {
            /* directly calling remove() does not remove body from list - BUG??*/
            ((GameObject)fixtureB.Body.UserData).state = State.REMOVED;
            return true;
        }

        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);
            if (blackHole == false)
            {
                ScreenManager.SpriteBatch.Begin();
                ScreenManager.SpriteBatch.DrawString(ScreenManager.Font, "Use your arrows to move", new Vector2(40, 40), Color.Red);
                ScreenManager.SpriteBatch.DrawString(ScreenManager.Font, "Push the benche with your body to the wall and jump on it", new Vector2(40, 80), Color.Red);
                ScreenManager.SpriteBatch.End();
            }
        }
    }
}
